﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace ReflectionAndMetadata
{
    class ParallelProgramming
    {
        public static async Task Show()
        {
            /* //IEnumerable<int> numbers = Enumerable.Range(3, 100000 - 3);
             //var parallelQuery = from n in numbers
             //                    where Enumerable.Range(2, (int)Math.Sqrt(n)).All(i => n % i > 0)
             //                    select n;
             //int[] primes = parallelQuery.ToArray();
             //Console.WriteLine(string.Join(',',primes));

             //var arr = "abcdef".AsParallel().Select(c => char.ToUpper(c)).ToArray();
             //Console.WriteLine(string.Join(',',arr));

             if (!File.Exists("WordLookup.txt"))
             {
                 File.WriteAllText("WordLookup.txt", await new HttpClient().GetStringAsync("http://www.albahari.com/ispell/allwords.txt"));
             }

             var wordLookup = new HashSet<string>(File.ReadAllLines("WordLookup.txt"),StringComparer.InvariantCultureIgnoreCase);

             //var random = new Random();
             //string[] wordList = wordLookup.ToArray();
             //string[] wordsToTest = Enumerable.Range(0, 1000000)
             //       .Select(i => wordList[random.Next(wordList.Length)]).ToArray();

             //wordsToTest[12345] = "woozsh";
             //wordsToTest[23456] = "wubside";

             //var query = wordsToTest.AsParallel().Select((word, index) => new { word, index })
             //    .Where(w => !wordLookup.Contains(w.word))
             //    .OrderBy(w => w.index);

             //foreach (var mistake in query)
             //{
             //    Console.WriteLine(mistake.word + " - index=" + mistake.index);
             //}

             // Using ThreadLocal<T>
             var localRandom = new ThreadLocal<Random>(() => new Random(Guid.NewGuid().GetHashCode()));
             string[] wordList = wordLookup.ToArray();
             string[] wordsToTest = Enumerable.Range(0,1000000)
                 .Select(i => wordList[localRandom.Value.Next(0,wordList.Length)])
                 .ToArray();

             wordsToTest[12345] = "woozsh";
             wordsToTest[23456] = "wubside";

             var query = wordsToTest.AsParallel().Select((word, index) => new { word, index })
              .Where(w => !wordLookup.Contains(w.word))
              .OrderBy(w => w.index);
             foreach (var mistake in query)
             {
                 Console.WriteLine(mistake.word + " - index=" + mistake.index);
             }*/

            /*// Functional Purity
            int i = 0;
            // 这一行输出的顺序是杂乱的，也就是多个线程会互相影响的修改 i 的值
            var query = from n in Enumerable.Range(0, 999).AsParallel()
                        select n * i++;
            // 下面的这一行是线程安全的，并且是有顺序的输出了结果
            var indexQuery = Enumerable.Range(0, 999).AsParallel().Select((n, i) => n * i++);

            foreach (var item in indexQuery)
            {
                Console.WriteLine(item);
            }*/

            /*// Setting the Degree of Parallelism
            var q = Enumerable.Range(0,999).AsParallel().WithDegreeOfParallelism(4);

            // Changing the degree of parallelism
            // 下面的方法 首先使用两个并发线程处理 是否是空白字符，然后在使用三个线程处理把字符转大写。 期间必须调用一次 AsParallel();
            "The Quick Brown Fox".AsParallel().WithDegreeOfParallelism(2)
                .Where(c => !char.IsWhiteSpace(c))
                .AsParallel().WithDegreeOfParallelism(3)
                .Select(c => char.ToUpper(c));

            // Cancellation
            IEnumerable<int> million = Enumerable.Range(3, 1000000);
            var cancelSource = new CancellationTokenSource();
            var primeNumberQuery = from n in million.AsParallel().WithCancellation(cancelSource.Token)
                                   where Enumerable.Range(2,(int)Math.Sqrt(n)).All(i=>n%i>0)
                                   select n;
            // 线程并行运行，一旦线程开始了，在取消也需要等待线程都执行完后才能取消，除非在其他线程都还没有开始之前取消了则就不会执行了
            new Thread(() =>
            {
                Thread.Sleep(100);
                cancelSource.Cancel();
            }).Start();

            try
            {
                // Start query running
                int[] primes = primeNumberQuery.ToArray();
                // We'll never get here because the other thread will cancel us.
                foreach (int i in primes)
                    Console.WriteLine(i);
            }
            catch (OperationCanceledException ex)
            {
                Console.WriteLine("Query canceled");
            }
*/

            /*// Optimizing PLINQ 最优化并行
            //"abcdef".AsParallel().WithDegreeOfParallelism(2).Select(c => char.ToUpper(c)).ForAll(Console.Write);

            // 1 按照 Range算法分数据到不同的线程中
            var list = ParallelEnumerable.Range(0, 10000);

            // 2 按照 Chunk 算法分数据到不同的线程中
            int[] numbers = { 3, 4, 5, 6, 7, 8, 9 };
            var parallelQuery = Partitioner.Create(numbers, true).AsParallel().Where(x => x > 5);

            numbers.AsParallel().Aggregate(
                () => 0,
                (localTotal, n) => localTotal + n,
                (mainTot, localTot) => mainTot + localTot,
                finalResult => finalResult);
*/
            /*// 1 Parallel.Invoke
            // 下面的方法会开启两个线程同时下载两个网站的两个页面，这个看起来就是在 Parallel.Inovke 方法中传递了两个委托而已，但是它比单独的委托效率高的多。
            Parallel.Invoke(
                () => new WebClient().DownloadFile("http://www.linqpad.net", "lp.html"),
                () => new WebClient().DownloadFile("http://microsoft.com", "ms.html"));

            ConcurrentBag<string> data = new ConcurrentBag<string>();
            Parallel.Invoke(
                ()=>data.Add(new WebClient().DownloadString("http://www.foo.com")),
                ()=>data.Add(new WebClient().DownloadString("http://www.far.com")));*/

            // 2 Parallel.For and Parallel.ForEach
            //var keyPairs = new string[6];
            //Parallel.For(0, keyPairs.Length, i => keyPairs[i]=RSA.Create().ToXmlString(true));
            //// File.WriteAllText("keyPairs.xml",keyPairs.ToString());
            //// 使用 PLINQ的方式
            //foreach (var keyPair in keyPairs)
            //{
            //    File.AppendAllLines("keyPairs.xml", keyPairs);
            //}

            //string[] keyPairs = ParallelEnumerable.Range(0,6).Select(i=>RSA.Create().ToXmlString(true)).ToArray();

            // 使用 Parallel.For() 方法时候只在最外层使用，不要在内层使用
            // 在 Parallel.ForEach 循环中使用索引
            //Parallel.ForEach("Hello,World", (c, state, i) =>
            //{
            //    Console.WriteLine(c.ToString() + i+"state:"+state);
            //});

            if (!File.Exists("WordLookup.txt"))
                new WebClient().DownloadFile("http://www.albahari.com/ispell/allwords.txt", "WordLookup.txt");

            var wordLookup = new HashSet<string>(File.ReadAllLines("WordLookup.txt"), StringComparer.InvariantCultureIgnoreCase);
            var random = new Random();
            string[] wordList = wordLookup.ToArray();
            string[] wordsToTest = Enumerable.Range(0, 1000000).Select(i => wordList[random.Next(0, wordList.Length)]).ToArray();

            wordsToTest[12345] = "王静安";
            wordsToTest[23456] = "wja";
            var misspellings = new ConcurrentBag<Tuple<int, string>>();
            Parallel.ForEach(wordsToTest, (word, state, i) =>
            {
                if (!wordLookup.Contains(word))
                    misspellings.Add(Tuple.Create((int)i, word));
            });

            foreach (var mistake in misspellings)
            {
                Console.WriteLine(mistake.Item2 + " - index=" + mistake.Item1);
            }

            foreach (char c in "Hello,world")
                if (c == ',')
                    break;
                else
                    Console.Write(c);

            Console.WriteLine();
            Parallel.ForEach("Hello,world", (c, loopState) =>
            {
                if (c == ',')
                    loopState.Break();
                else
                    Console.Write(c);
            });


        }

        public static void Shw2()
        {

            /* // 使用指针来优化性能，避免过多的垃圾回收
             unsafe int Sum(int* numbers, int count)
             {
                 int total = 0;
                 for (int i = 0;i<count;i++) total += numbers[i];
                 return total;
             }

             int* numbers = stackalloc int[1000];
             int total = Sum(numbers, 1000);*/

            /*// 但是在C#中使用指针是不安全的，编译会报错，我们可以使用span来代替
            Span<int> numbers = stackalloc int[1000];
            for (int i = 0; i < 1000; i++)
            {
                numbers[i] = i;
            }
            int Sum(ReadOnlySpan<int> numbers)
            {
                int total = 0;
                int len = numbers.Length;
                for (int i = 0; i < len; i++) total += numbers[i];
                return total;
            }
            int total = Sum(numbers);
            Console.WriteLine(total);*/

            //var source = "The quick brown fox".AsSpan();
            //var ptr = Marshal.AllocHGlobal(source.Length*sizeof(char));
            //try
            //{
            //    var unmanaged = new Span<char>((char*)ptr, source.Length);
            //    source.CopyTo(unmanaged);
            //    foreach (char c in unmanaged)
            //        Console.WriteLine();
            //}
            //finally
            //{
            //    Marshal.FreeHGlobal(ptr);
            //}

            /* var obj = new Uri("https://www.linqpad.net");
             if (obj is Uri { Scheme.Length: 5 }) Console.WriteLine("The length is 5");
             if (obj is Uri { Scheme: { Length: 5 } }) Console.WriteLine("The length is 5");

             // CallerArgumentException
             void Print(double number, [CallerArgumentExpression("number")] string expr = null) => Console.WriteLine(expr);

             Print(Math.PI * 2);

             // Init-only setters
             // 在属性中使用 init关键字修饰的字段只能在对象初始化时候赋值，其他都不能赋值。
             string GetWeightCategory(decimal bmi) => bmi switch
             {
                 < 18.5m => "underweight",
                 < 25m => "normal",
                 < 30m => "overweight",
                 _ => "obese"
             };

             bool IsVowel(char c) => c is 'a' or 'e' or 'i' or 'o' or 'u';
             bool IsLetter(char c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';

             string? s=null;
             s ??= "Hello,world";

             int cardNumber = 12;string suite = "spades";
             string cardName = (cardNumber, suite) switch
             {
                 (13, "spades") => "King of spades",
                 (13, "clubs") => "King of clubs",
                 (12, "Spades") => "Queue of Spades",
                 _=>"other"
             };

             string s1 = null;
             string? s2 = null;

             void Foo(string? s)=>Console.WriteLine(s?.Length);*/


            //Console.WriteLine(Cube(3));
            //Console.WriteLine(Cube(4));
            //Console.WriteLine(Cube(5));
            //int Cube(int value) => value * value * value;

            //int a = int.MinValue;
            //a--;
            //Console.WriteLine(a==int.MaxValue);

            // 检查整数是否超出
            //int a = 1000000;
            //int b = 1000000;
            //int c = checked(a*b); // 只检查这一个

            //// 检查多个表达式
            //checked
            //{
            //    c= a* b;
            //    int d = checked(a* b);
            //}
            //int c = a * b;
            //Console.WriteLine(c);

            //int x = int.MaxValue;
            //int y = unchecked(x + 1);
            //unchecked
            //{
            //    int z = x + 1;
            //}
            // 对于常量表达式始终会检查是否内存溢出的
            //int x = int.MaxValue + 1;
            //int y = unchecked(int.MaxValue + 1);

            //short x =1,y =1;
            //// 对于 short 类型没有自己的加减乘除算法，C# 会隐士的把他们转为 int 类型进行数学运算，所以下面一行代码会报错
            ////short z = x + y;
            //short z = (short)(x + y);


            //Console.WriteLine(1.0/0.0);
            //Console.WriteLine(-1.0/0.0);
            //Console.WriteLine(1.0/-0.0);
            //Console.WriteLine(-1.0/-0.0);
            //Console.WriteLine(double.IsNaN(0.0/0.0));

            /* float x = 0.1f;
             Console.WriteLine(10 * x);
             Console.WriteLine(x + x + x + x + x + x + x + x + x + x);
             double y = 0.1d;
             Console.WriteLine(10 * y);
             Console.WriteLine(y + y + y + y + y + y + y + y + y + y);
             decimal z = 0.1m;
             Console.WriteLine(10 * z);
             Console.WriteLine(z + z + z + z + z + z + z + z + z + z);

             decimal m = 1M / 6m;
             double d = 1.0 / 6.0;
             Console.WriteLine(m);
             Console.WriteLine(d);
             decimal notQuiteWholeM = m + m + m + m + m + m;
             Console.WriteLine(notQuiteWholeM);
             double notQuiteWholeD = d + d + d + d + d + d;
             Console.WriteLine(notQuiteWholeD);*/

            /*  string escaped = "First Line\r\nSecond Line";
              string verbatim = @"First Line
  Second Line";
              Console.WriteLine(escaped==verbatim);

              string s = $"255 in hex is {byte.MaxValue:X2}";
              Console.WriteLine(s);*/

            //int[] a = new int[1000];
            //Console.WriteLine(a[123]);

            //int[] b = null;

            /*   int[,] matrix = new int[3, 3];
               for (int i = 0; i < matrix.GetLength(0); i++)
               {
                   for (int j = 0; j < matrix.GetLength(1); j++)
                   {
                       matrix[i, j] = i * 3 + j;
                   }
               }

               int[,] matrix2 = new int[,]
               {
                   {0,1,2 },
                   {3,4,5},
                   {6,7,8}
               };*/

            // 简单的初始化多维数组
            char[] vowels = { 'a', 'e', 'i', 'o', 'u' };
            int[,] rectangularMatrix =
            {
                {0,1,2 },
                {3,4,5},
                {6,7,8},
            };

            int[][] jaggedMatrix =
            {
                new int[]{0,1,2 },
                new int[]{3,4,5},
                new int[]{6,7,8},
            };


            Console.ReadLine();
        }
    }

    struct Point
    {
        public int X, Y;
        //public readonly void ResetX() => X = 0;// readonly 修饰方法时候，这个方法是只读的，只读方法内部不能修改字段的值。
    }

    class MyClassCSharp7X
    {
        [field: NonSerialized]
        public int MyProperty { get; set; }

        public void Show()
        {
            //unsafe
            //{
            //    int* pointer = stackalloc int[] { 1, 2, 3 };
            //}
            Span<int> arr = stackalloc int[] { 1, 2, 3 };

            var now = DateTime.Now;
            var tuple = (now.Hour, now.Minute, now.Second);
            int million = 1_000_000;

            var b = 0b1010_1011_1100_1101_1110_1111;

        }
        void Foo(object x)
        {
            if (x is string s)
            {
                Console.WriteLine(s.Length);
            }

        }


    }

    public class Person
    {
        string name;
        public string Name
        {
            get => name;
            set => name = value ?? "";
        }
        public Person(string name) => Name = name;

        public int Age { get; set; }


        ~Person() => Console.WriteLine("finalize");
    }
}
